package com.example.modelfunction.shrio;

import java.util.*;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.example.modelfunction.shrio.rediscacheext.ShrioRedisCacheManager;
import com.example.base.Redis.RedisAccessManager;
import com.example.modelfunction.shrio.rediscacheext.RedisSessionDAO;
import com.example.modelfunction.shrio.usermanager.permission.MyRolePermissionResolver;
import com.example.modelfunction.shrio.usermanager.permission.UrlPermissionResolver;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,KickoutSessionFilter mfilter) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        HashMap<String,Filter> hashMap=new HashMap<String,Filter>();
        hashMap.put("kickout",mfilter);
        shiroFilterFactoryBean.setFilters(hashMap);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
//        filterChainDefinitionMap.put("/login", "anon");
//        filterChainDefinitionMap.put("/static/index.html","anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/ajaxLogin", "anon");

        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "kickout,authc");

//        List<Permission> permissionList = permissionService.findAllPermission()
//        permissionList?.each {current_Permission ->
//                //规则："roles[admin,user]", "perms[file:edit]"
//                filterChainDefinitionMap.put(current_Permission?.getUrl(),"perms[" + current_Permission?.getPermission() + "]")
//            logger.info("从数据库加载的拦截器规则：资源路径： " + current_Permission?.getUrl() + " ,需要权限：" +current_Permission?.getPermission())
//        }
//        filterChainDefinitionMap.put("/machineAction/**", "perms[WAREHOUSE]");
//        filterChainDefinitionMap.put("/user", "roles[WAREHOUSE]");
//        filterChainDefinitionMap.put("/machineAction/**", "roles[admin]");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("static/index.html");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/dailySalesStatisticsAction/saleHome");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public CustomRealm myShiroRealm(HashedCredentialsMatcher matcher){
        CustomRealm myShiroRealm = new CustomRealm();
        myShiroRealm.setPermissionResolver(new UrlPermissionResolver());//增加permission管理
        myShiroRealm.setRolePermissionResolver(new MyRolePermissionResolver()); //增加role管理
        myShiroRealm.setCredentialsMatcher(matcher);
        return myShiroRealm;
    }

    /**
     * Shiro生命周期处理器 * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


   /* @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }*/
   @Bean DefaultWebSessionManager sessionManager(SessionDAO redisSessionDAO)
   {
       DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
       Collection<SessionListener> listeners = new ArrayList<SessionListener>();
       //配置监听
       listeners.add(new ShiroSessionListener());
       sessionManager.setSessionListeners(listeners);
       sessionManager.setSessionDAO(redisSessionDAO);
       sessionManager.setGlobalSessionTimeout(1800000);
      // sessionManager.setSessionIdCookie(sessionIdCookie());
       return sessionManager;
   }
   @Bean
   public DefaultWebSecurityManager securityManager(CustomRealm realm, ShrioRedisCacheManager cache, DefaultWebSessionManager sessionManager ) {
       DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
       securityManager.setRealm(realm);

       securityManager.setSessionManager(sessionManager);

       securityManager.setCacheManager(cache);
       return securityManager;
   }
   @Bean
    public KickoutSessionFilter kickoutSessionFilter(SessionDAO redisSessionDAO, ShrioRedisCacheManager cache, DefaultWebSessionManager sessionManager){
        KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();
        //使用cacheManager获取相应的cache来缓存用户登录的会话；用于保存用户—会话之间的关系的；
        //这里我们还是用之前shiro使用的ehcache实现的cacheManager()缓存管理
        //也可以重新另写一个，重新配置缓存时间之类的自定义缓存属性
        kickoutSessionFilter.setCacheManager(cache);
        //用于根据会话ID，获取会话进行踢出操作的；
        kickoutSessionFilter.setSessionManager(sessionManager);
        //是否踢出后来登录的，默认是false；即后者登录的用户踢出前者登录的用户；踢出顺序。
        kickoutSessionFilter.setKickoutAfter(false);
        //同一个用户最大的会话数，默认1；比如2的意思是同一个用户允许最多同时两个人登录；
        kickoutSessionFilter.setMaxSession(5);
        //被踢出后重定向到的地址；
        kickoutSessionFilter.setKickoutUrl("/login");
        return kickoutSessionFilter;
    }
    @Bean
    public ShrioRedisCacheManager shrioCacheManager(RedisAccessManager redis){
        ShrioRedisCacheManager shrioRedisCacheManager = new ShrioRedisCacheManager();
        shrioRedisCacheManager.setRedisAccessManager(redis);
        //redis中针对不同用户缓存
        shrioRedisCacheManager.setPrincipalIdFieldName("username");
        //用户权限信息缓存时间
        shrioRedisCacheManager.setExpire(200000);
        return shrioRedisCacheManager;
    }

    /**
     * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
     * MemorySessionDAO 直接在内存中进行会话维护
     * EnterpriseCacheSessionDAO  提供了缓存功能的会话维护，默认情况下使用MapCache实现，内部使用ConcurrentHashMap保存缓存的会话。
     * @return
     */
    @Bean
    public SessionDAO sessionDAO(RedisAccessManager redis) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisAccessManager(redis);
        //session在redis中的保存时间,最好大于session会话超时时间
        redisSessionDAO.setExpire(2000000);
        return redisSessionDAO;
    }
   /* @Bean
    public DefaultWebSessionManager sessionManager(SessionDAO redisSessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO);
        return sessionManager;
    }*/
    /**
     * 配置保存sessionId的cookie
     * 注意：这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
     * 默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid
     * @return
     */
   /* @Bean("sessionIdCookie")
    public SimpleCookie sessionIdCookie(){
        //这个参数是cookie的名称
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：

        //setcookie()的第七个参数
        //设为true后，只能通过http访问，javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //maxAge=-1表示浏览器关闭时失效此Cookie
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }
    @Bean
    public SimpleCookie remeberMeCookie(){
        //cookie名称;对应前端的checkbox的name = rememberMe
        SimpleCookie scookie=new SimpleCookie("rememberMe");
        //记住我cookie生效时间30天 ,单位秒  [10天]
        scookie.setMaxAge(864000);
        return scookie;
    }
    @Bean
    public CookieRememberMeManager rememberMeManager(){

        CookieRememberMeManager cookieRememberMeManager=new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(remeberMeCookie());
        return cookieRememberMeManager;
    }*/

    /**
     * 配置cookie记住我管理器
     * @return
     */


    @Bean(name = "shiroDialect")

    public ShiroDialect shiroDialect() {

        return new ShiroDialect();

    }
        /**
         * 凭证匹配器 （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
         * 所以我们需要修改下doGetAuthenticationInfo中的代码,更改密码生成规则和校验的逻辑一致即可; ）
         *
         * @return
         */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(ShrioRedisCacheManager cahceManager) {
        HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(cahceManager);
        hashedCredentialsMatcher.setHashAlgorithmName("sha");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1);// 散列的次数，比如散列两次，相当于 // md5(md5(""));
        return hashedCredentialsMatcher;
    }


}
